#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h> 
#include <pthread.h>

#define DFL_PORT 8081
#define BACKLOG 5

class Prama{
  public:
    int sock;
    std::string ip;
    int port;
  public:
    Prama(int _sock,std::string _ip,int _port)
      :sock(_sock),ip(_ip),port(_port)
    {}

    ~Prama()
    {}
};

//TCP是面向连接的，所以需要在正式发送数据之前，需要建立连接
class TcpServer{
  private:
    int port;
    int lsock;//listensock,监听文件描述符
  public:
    TcpServer(int _port = DFL_PORT):port(_port),lsock(-1)
    {}
    
    bool InitTcpServer()
    {
      //创建套接字
      lsock = socket(AF_INET,SOCK_STREAM,0);
      //小于0说明创建失败
      if(lsock< 0){  
        std::cerr << "socket error" << std::endl;
        return false;
      }
      
      struct sockaddr_in local;
      memset(&local,0,sizeof(local));
      local.sin_family = AF_INET;
      local.sin_port = htons(port);
      local.sin_addr.s_addr = INADDR_ANY;

      //绑定
      if(bind(lsock,(struct sockaddr*)&local,sizeof(local)) < 0){
        std::cerr << "bind error" << std::endl;
        return false;
      }
      
      //设置监听状态,因为TCP是全连接
      if(listen(lsock,BACKLOG) < 0) {
        std::cerr << "listen error" << std::endl;
        return false;
      }
      
      return true;
    }

    static void *HanderRequest(void *arg)
    {
      Prama *p = (Prama*)arg;
      pthread_detach(pthread_self());//分离线程
      
      Service(p->sock,p->ip,p->port);
      
      close(p->sock);
      delete p;
      return nullptr;
    }

    void Loop()
    {
      //signal(SIGCHLD,SIG_IGN);
      for(;;){
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        //lsock作用是获取新连接，sock的作用是服务新连接(读取数据，分析处理数据，写入数据)
        int sock = accept(lsock,(struct sockaddr*)&peer,&len);
        if(sock < 0){//获取链接失败
          std::cout << "accept error" << std::endl;
          continue;//继续去找新链接
        }
       
        //三、线程池方案
        //ThreadPoll.hpp,蛋哥码云仓库获取        


       // //二、创建线程法
       // pthread_t tid; 
       // std::string ip = inet_ntoa(peer.sin_addr);
       // int port = ntohs(peer.sin_port);

       // Prama *p = new Prama(sock,ip,port);//避免执行Hander方法时下一个线程太快覆盖原sock的数据，所以new一个sock，这样每个p指向的sock都是不同的
       // pthread_create(&tid,nullptr,HanderRequest,p);


       //一、创建进程法
       // pid_t id = fork();
       // if(id == 0){ //child, pipe!
       //   close(lsock);//因为是子进程去提供服务的，而不是父进程，所以不需要父进程监听描述符，避免读取文件的时候出错
       //   if(fork() > 0){//子进程再去fork创建子进程
       //     exit(0);//创建成功就终止父进程，让孙子进程去执行服务，此时孙子进程就是孤儿进程，被操作系统领养，所以孙子进程退出是有操作系统在等，不会出现僵尸进程，而爷爷进程就可以去找新的链接了
       //   }
       //   std::string ip = inet_ntoa(peer.sin_addr);
       //   int port = ntohs(peer.sin_port);
       //   std::cout<<"get a new link->"<<sock<<"["<<ip<<"]:"<<port<<std::endl;
       //   Service(sock,ip,port);//孙子进程服务
       //   exit(0);
       // }
       // //waitpid();//阻塞等待不满足要求，父进程必须去获取新链接为他们提供服务，所以不能用这个
       // 
       // //father
       // close(sock);//父进程关闭sock去寻找下一个链接
       // waitpid(-1,nullptr,0);//这里不会发生阻塞，因为父亲进程直接exit了，那么爷爷进程在这里等待就不会产生阻塞
      }
    }

    static void Service(int sock,std::string ip,int port)
    {
      char buffer[1024];
      while(true){
        ssize_t size = read(sock,buffer,sizeof(buffer)-1);
        if(size > 0){//读取到文件
          buffer[size] = 0;
          std::cout << ip << ":" << port << "#" << buffer << std::endl;
          
          write(sock,buffer,size);//将客户端发过来的数据回显给客户端
        }
        else if(size == 0){//读到文件结尾或者写端关闭
          std::cout << ip << ":" << port << "close" << buffer << std::endl;
          break;
        }
        else{//读取出错
          std::cerr << sock << "read error" << std::endl;
          break;
        }
      }
      close(sock);
      std::cout << "service done" << std::endl;
    }

    ~TcpServer()
    {
      if(lsock >= 0)
        close(lsock);
    }
};
